/*
Module : DIBIMAGE.CPP
Purpose: Implementation for an MFC class that encapsulates DIBs
         and supports a variety of image manipulation functions on it
Created: PJN / 23-07-1997
History: PJN / 23-07-1999 1. Unicode enabled the code + provision of Unicode configurations in the workspace
                          2. ColorsUsed function has now been implemented

                          Major update by Fabio Angelini. Changes include:

                          1. Inclusion of a C3By3MeanFilter.
                          2. Tidy up of how CDibImage disposes of its resources
                          3. Addition of a Detach method
                          4. Pixel access is now supported for 1, 4, 8 and 24 bit pixel formats
                          5. Function to convert from a HBITMAP to a DIB
                          6. Function to convert to a HBITMAP
                          7. Function to screen capture a specified area of a specified window
                          8. Function to convert to 24 bits.
                          9. Unified all the low-level API to manage DIB files into as single source code

         PJN / 06-09-1999 1. Provision of a IsRunLengthEncoded function.
                          2. Updates to the DataFormatSupported function to prevent access violations in the
                          dibedit sample application

         PJN / 12-09-1999 1. Added support for Jpeg loading or saving by means of the Intel Jpeg Library

         PJN / 21-09-1999 1. Fixed a bug in the function WindowToDIB which is used by CDibImage::CopyFromWindow

         PJN / 03-10-1999 1. Optimized memory usage when loading and saving JPEG images from file.

         PJN / 25-01-2000 1. Updated code to use Intel Jpeg Library v1.1
                      



Copyright (c) 1997 - 2000 by PJ Naughter.  
All rights reserved.

*/

/////////////////////////////////  Includes  //////////////////////////////////
#include "stdafx.h"
#include "math.h"
#include "dibimage.h"
#ifndef DIBIMAGE_NO_JPEG
//#include "jpegapi.h"
#endif 
//#include "resource.h"




///////////////////////////////// defines /////////////////////////////////////
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif



//////////////////////////////// Implementation ///////////////////////////////
C3By3Filter::C3By3Filter()
{
  //Default filter is a filter which will not do anything to the image
  for (int i=0; i<3; i++)
    for (int j=0; j<3; j++)
      m_nValues[i][j] = 0;
      
  m_nValues[1][1] = 1;
  m_nDivision = 1;
  m_nBias = 0;
}


COLORREF C3By3Filter::Filter(CDibImage& dibImage, LPSTR lpDibBits, int x, int y)
{
  int r = 0;
  int g = 0;
  int b = 0;
  for (int i=0; i<3; i++)
  {
    for (int j=0; j<3; j++)
    {
      COLORREF c;
      if (dibImage.GetPixel(x+i-1, y+j-1, c, lpDibBits))
      {
        r += (m_nValues[i][j] * GetRValue(c));
        g += (m_nValues[i][j] * GetGValue(c));
        b += (m_nValues[i][j] * GetBValue(c));
      }
    }
  }

  ASSERT(m_nDivision);
  r = min(r/m_nDivision + m_nBias, 255);
  g = min(g/m_nDivision + m_nBias, 255);
  b = min(b/m_nDivision + m_nBias, 255);
  r = max(r, 0);
  g = max(g, 0);
  b = max(b, 0);

  return RGB(r, g, b);
}


C3By3MedianFilter::C3By3MedianFilter()
{
}


COLORREF C3By3MedianFilter::Filter(CDibImage& dibImage, LPSTR lpDibBits, int x, int y)
{
  int nPixels = 0;

  for (int k=0; k<9; k++)
    m_Ordered[k] = 0;

  for (int i=0; i<3; i++)
  {
    for (int j=0; j<3; j++)
    {
      COLORREF c;
      if (dibImage.GetPixel(x+i-1, y+j-1, c, lpDibBits))
      {
        m_Ordered[nPixels] = c;
        nPixels++;
      }
    }
  }

  qsort(&m_Ordered, nPixels, sizeof(COLORREF), CompareFunc);
  return m_Ordered[nPixels/2];
}


int C3By3MedianFilter::CompareFunc(const void *elem1, const void *elem2)
{
  COLORREF c1 = *(COLORREF*)elem1;
  COLORREF c2 = *(COLORREF*)elem2;
   
  if (c1 == c2)
    return 0;
  else 
  {
    BYTE r1 = GetRValue(c1);
    BYTE g1 = GetGValue(c1);
    BYTE b1 = GetBValue(c1);
    BYTE r2 = GetRValue(c2);
    BYTE g2 = GetGValue(c2);
    BYTE b2 = GetBValue(c2);
    
    if ((r1 + g1 + b1) > (r2 + g2 + b2))
      return 1;
    else
      return -1;
  }
}


C3By3MeanFilter::C3By3MeanFilter()
{
}


COLORREF C3By3MeanFilter::Filter(CDibImage& dibImage, LPSTR lpDibBits, int x, int y)
{
  COLORREF r=0,g=0,b=0;
  COLORREF co;
  COLORREF c;

  dibImage.GetPixel(x, y, co, lpDibBits); 
  for (int j=-1; j<2; j++)
  {
    for (int i=-1; i<2; i++)
    {
      if ( dibImage.GetPixel(x+i, y-j, c, lpDibBits) )
      {
        r += GetRValue(c);
        g += GetGValue(c);
        b += GetBValue(c);
      }
    }
  }

  return RGB(r/9, g/9, b/9 );
}



C5By5Filter::C5By5Filter()
{
  //Default filter is a filter which will not do anything to the image
  for (int i=0; i<5; i++)
    for (int j=0; j<5; j++)
      m_nValues[i][j] = 0;
  
  m_nValues[2][2] = 1;
  m_nDivision = 1;
  m_nBias = 0;
}


COLORREF C5By5Filter::Filter(CDibImage& dibImage, LPSTR lpDibBits, int x, int y)
{
  int r = 0;
  int g = 0;
  int b = 0;
  for (int i=0; i<5; i++)
  {
    for (int j=0; j<5; j++)
    {
      COLORREF c;
      if (dibImage.GetPixel(x+i-2, y+j-2, c, lpDibBits))
      {
        r += (m_nValues[i][j] * GetRValue(c));
        g += (m_nValues[i][j] * GetGValue(c));
        b += (m_nValues[i][j] * GetBValue(c));
      }
    }
  }

  ASSERT(m_nDivision);
  r = min(r/m_nDivision + m_nBias, 255);
  g = min(g/m_nDivision + m_nBias, 255);
  b = min(b/m_nDivision + m_nBias, 255);
  r = max(r, 0);
  g = max(g, 0);
  b = max(b, 0);

  return RGB(r, g, b);
}


C7By7Filter::C7By7Filter()
{
  //Default filter is a filter which will not do anything to the image
  for (int i=0; i<7; i++)
    for (int j=0; j<7; j++)
      m_nValues[i][j] = 0;
  
  m_nValues[3][3] = 1;
  m_nDivision = 1;
  m_nBias = 0;
}


COLORREF C7By7Filter::Filter(CDibImage& dibImage, LPSTR lpDibBits, int x, int y)
{
  int r = 0;
  int g = 0;
  int b = 0;
  for (int i=0; i<7; i++)
  {
    for (int j=0; j<7; j++)
    {
      COLORREF c;
      if (dibImage.GetPixel(x+i-3, y+j-3, c, lpDibBits))
      {
        r += (m_nValues[i][j] * GetRValue(c));
        g += (m_nValues[i][j] * GetGValue(c));
        b += (m_nValues[i][j] * GetBValue(c));
      }
    }
  }

  ASSERT(m_nDivision);
  r = min(r/m_nDivision + m_nBias, 255);
  g = min(g/m_nDivision + m_nBias, 255);
  b = min(b/m_nDivision + m_nBias, 255);
  r = max(r, 0);
  g = max(g, 0);
  b = max(b, 0);

  return RGB(r, g, b);
}



CUndoNode::CUndoNode(CDibImage* pImage, const CString& sDescription)
{
  m_pImage = pImage;
  m_sDescription = sDescription;
}


CUndoNode::~CUndoNode()
{
  delete m_pImage;
  m_pImage = NULL;
}



CDibImage::CDibImage()
{
  m_hDib = NULL;
  m_pWorkingArea = NULL;
  m_nWidth = 0;
  m_nHeight = 0;
  m_nScanWidth = 0;
  m_nBitsPerPixel = 0;
  m_Pal = NULL;

  m_nUndoSize = 4;  //By default, we support 4 levels of undo, if you
  //want more (at the expense of memory usage) just
  //call SetUndoSize will the level you want
}


CDibImage::CDibImage(const CDibImage& ds)
{
  m_hDib = NULL;
  m_pWorkingArea = NULL;
  m_nWidth = 0;
  m_nHeight = 0;
  m_nScanWidth = 0;
  m_nBitsPerPixel = 0;
  m_Pal = NULL;

  m_nUndoSize = 4;  //By default, we support 4 levels of undo, if you
  //want more (at the expense of memory usage) just
  //call SetUndoSize will the level you want

  Attach((HDIB) CopyHandle(ds.m_hDib));
  SetWorkingArea(ds.m_pWorkingArea->Clone());
}


CDibImage::~CDibImage()
{
  Destroy();

  //empty the redo stack
  int i=0;
  for (; i<m_RedoStack.GetSize(); i++)
  {
    CUndoNode* pNode = m_RedoStack.GetAt(i);
    delete pNode;
    pNode = NULL;
  }  
  m_RedoStack.SetSize(0);

  //empty the undo stack
  for (i=0; i<m_UndoStack.GetSize(); i++)
  {
    CUndoNode* pNode = m_UndoStack.GetAt(i);
    delete pNode;
    pNode = NULL;
  }  
  m_UndoStack.SetSize(0);
}


CDibImage& CDibImage::operator=(const CDibImage& ds)
{
  Attach((HDIB) CopyHandle(ds.m_hDib));
  SetWorkingArea(ds.m_pWorkingArea->Clone());
  return *this;
}


BOOL CDibImage::Create(CSize size, WORD nBitCount,HDIB sourseDib)
{
  Destroy();

  BOOL bSuccess = TRUE;
  CWindowDC dcScreen(NULL);

  // should not already have palette
  ASSERT(m_Pal == NULL);

  CPalette pal;
  ::CreateDIBPalette(sourseDib, &pal);
  m_Pal = (HPALETTE) pal.Detach();

  // Select specified palette
  if(m_Pal ==NULL)
	m_Pal = ::CreateHalftonePalette(dcScreen.m_hDC);
  if (m_Pal == NULL)
    return FALSE;

  // Fill in the BITMAPINFO Structure
  BITMAPINFO bmi;
  bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  bmi.bmiHeader.biWidth = size.cx;
  bmi.bmiHeader.biHeight = size.cy;
  bmi.bmiHeader.biPlanes = 1;
  bmi.bmiHeader.biBitCount = nBitCount;
  bmi.bmiHeader.biCompression = BI_RGB;
  bmi.bmiHeader.biSizeImage = 0;
  bmi.bmiHeader.biXPelsPerMeter = 0;
  bmi.bmiHeader.biYPelsPerMeter = 0;
  int nPaletteSize = ComputePaletteSize(nBitCount);
  bmi.bmiHeader.biClrUsed = nPaletteSize;
  bmi.bmiHeader.biClrImportant = nPaletteSize;

  m_nWidth = size.cx;
  m_nHeight = size.cy;
  m_nBitsPerPixel = nBitCount;
  m_nScanWidth = WIDTHBYTES(m_nWidth*m_nBitsPerPixel);
  m_pWorkingArea = new CRectWorkingArea(Rect());

  DWORD dwSize = sizeof(bmi) + m_nScanWidth*m_nHeight;
  m_hDib = (HDIB) ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwSize);
  if (m_hDib == NULL)
    return FALSE;
  LPSTR pDib = (LPSTR) ::GlobalLock((HGLOBAL) m_hDib);
  memcpy(pDib, &bmi, sizeof(bmi));
  ::GlobalUnlock((HGLOBAL) m_hDib);

  return bSuccess;
}


BOOL CDibImage::Create(CSize size, WORD nBitCount)
{
  Destroy();

  BOOL bSuccess = TRUE;
  CWindowDC dcScreen(NULL);

  // should not already have palette
  ASSERT(m_Pal == NULL);

  // Select specified palette
  m_Pal = ::CreateHalftonePalette(dcScreen.m_hDC);
  if (m_Pal == NULL)
    return FALSE;

  // Fill in the BITMAPINFO Structure
  BITMAPINFO bmi;
  bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  bmi.bmiHeader.biWidth = size.cx;
  bmi.bmiHeader.biHeight = size.cy;
  bmi.bmiHeader.biPlanes = 1;
  bmi.bmiHeader.biBitCount = nBitCount;
  bmi.bmiHeader.biCompression = BI_RGB;
  bmi.bmiHeader.biSizeImage = 0;
  bmi.bmiHeader.biXPelsPerMeter = 0;
  bmi.bmiHeader.biYPelsPerMeter = 0;
  int nPaletteSize = ComputePaletteSize(nBitCount);
  bmi.bmiHeader.biClrUsed = nPaletteSize;
  bmi.bmiHeader.biClrImportant = nPaletteSize;

  m_nWidth = size.cx;
  m_nHeight = size.cy;
  m_nBitsPerPixel = nBitCount;
  m_nScanWidth = WIDTHBYTES(m_nWidth*m_nBitsPerPixel);
  m_pWorkingArea = new CRectWorkingArea(Rect());

  DWORD dwSize = sizeof(bmi) + m_nScanWidth*m_nHeight;
  m_hDib = (HDIB) ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwSize);
  if (m_hDib == NULL)
    return FALSE;
  LPSTR pDib = (LPSTR) ::GlobalLock((HGLOBAL) m_hDib);
  memcpy(pDib, &bmi, sizeof(bmi));
  ::GlobalUnlock((HGLOBAL) m_hDib);

  return bSuccess;
}


int CDibImage::ComputePaletteSize(DWORD nBitCount)
{
  int nSize = 0;
  switch (nBitCount) 
  {
    case 1:  nSize = 2;     break;
    case 4:  nSize = 16;    break;
    case 8:	 nSize = 256;   break;
    case 16:
    case 24:
    case 32: nSize = 0;     break;
    default: ASSERT(FALSE); break;
  }
  return nSize;
}


void CDibImage::Destroy()
{
  DestroyWorkingArea();
  DestroyHPALLETTE();
  DestroyHDIB();
}


void CDibImage::DestroyWorkingArea()
{
  if (m_pWorkingArea)
  {
    delete m_pWorkingArea;
    m_pWorkingArea = NULL;
  }
}


void CDibImage::DestroyHDIB()
{
  if (m_hDib)
  {
    GlobalFree(m_hDib);
    m_hDib = NULL;
  }
}


void CDibImage::DestroyHPALLETTE()
{
  if ( m_Pal != NULL )
  {
    DeleteObject(m_Pal);
    m_Pal = NULL;
  }
}


BOOL CDibImage::Attach(HDIB hDib)
{
  Destroy();

  BOOL bSuccess = FALSE;
  if (hDib)
  {
    m_hDib = hDib;
    DWORD dwSize = GlobalSize(m_hDib);
    ASSERT(dwSize);
    LPSTR lpDib = (LPSTR) ::GlobalLock(m_hDib);
    ASSERT(lpDib);
    m_nWidth = ::DIBWidth(lpDib);
    m_nHeight = ::DIBHeight(lpDib);
    ::GlobalUnlock((HGLOBAL) m_hDib);
    m_nBitsPerPixel = GetBitsPerPixel();
    m_nScanWidth = WIDTHBYTES(m_nWidth*m_nBitsPerPixel);
    m_pWorkingArea = new CRectWorkingArea(Rect());
    CPalette pal;
    ::CreateDIBPalette(m_hDib, &pal);
    m_Pal = (HPALETTE) pal.Detach();
    bSuccess = TRUE;
  }
  return bSuccess;
}


HDIB CDibImage::Detach(void)
{
  DestroyWorkingArea();
  DestroyHPALLETTE();

  HDIB hDib = m_hDib;
  m_hDib = NULL;
  return hDib;
}


bool CDibImage::SetPalllette(HDIB hSourseDib)
{
   if (m_hDib ==NULL ||hSourseDib == NULL)
      return FALSE;
   
      /*
      * Get a pointer to the DIB memory, the first of which contains
      * a BITMAPINFO structure
   */
   /*
   lpBI = (LPBITMAPINFOHEADER) ::GlobalLock((HGLOBAL) m_hDib);
   if (lpBI == NULL)
      return FALSE;
   
   if (!IS_WIN30_DIB(lpBI))
   {
      ::GlobalUnlock((HGLOBAL) m_hDib);
      return FALSE;       // It's an other-style DIB (save not supported)
   }*/
   //*************************************************
	
   LPBITMAPINFO lpSourselog;
   lpSourselog= (LPBITMAPINFO)::GlobalLock((HGLOBAL) hSourseDib);

   LPBITMAPINFO lplog;
   lplog = (LPBITMAPINFO)::GlobalLock((HGLOBAL) m_hDib);
   int nCount =0;
   if(lpSourselog->bmiHeader.biBitCount == 1)
	   nCount =2;
   else if(lpSourselog->bmiHeader.biBitCount ==2)
		nCount = 4;
   else if(lpSourselog->bmiHeader.biBitCount ==4)
		nCount = 16;
	else if(lpSourselog->bmiHeader.biBitCount ==8)
		nCount =256;

	for(int i =0;i<nCount;i++)
	{
		lplog->bmiColors[i].rgbBlue = lpSourselog->bmiColors[i].rgbBlue;
		lplog->bmiColors[i].rgbGreen = lpSourselog->bmiColors[i].rgbGreen;
		lplog->bmiColors[i].rgbRed =  lpSourselog->bmiColors[i].rgbRed;
		lplog->bmiColors[i].rgbReserved=0;
	}
	/*
	lplog->bmiColors[0].rgbBlue = 0;
	lplog->bmiColors[0].rgbGreen = 0;
	lplog->bmiColors[0].rgbRed =  0;
	lplog->bmiColors[0].rgbReserved=0;*/
	::GlobalUnlock ((HGLOBAL)hSourseDib);
	::GlobalUnlock ((HGLOBAL)m_hDib);

	return true;
}

BOOL CDibImage::Load(LPCTSTR lpszPathName)
{
  CFile f;
  if (!f.Open(lpszPathName, CFile::modeRead | CFile::shareDenyWrite))
    return FALSE;

  //Determine what image type it is based on the filename extension
  TCHAR pszExt[_MAX_EXT];
  _tsplitpath(lpszPathName, NULL, NULL, NULL, pszExt);
  CString sExt(pszExt);   
  if ((sExt.CompareNoCase(_T(".bmp")) == 0) || (sExt.CompareNoCase(_T(".dib")) == 0))
		return Attach(::ReadDIBFile(f));
//#ifndef DIBIMAGE_NO_JPEG
//	else if ((sExt.CompareNoCase(_T(".jpg")) == 0) || (sExt.CompareNoCase(_T(".jpeg")) == 0))
//		return Attach(::ReadJPEGFile(f));
//#endif
  else
  {
    TRACE(_T("Could not detemine what file type to load based on the filename extension\n"));
    return FALSE;
  }
}


BOOL CDibImage::Load(HINSTANCE hInst, LPCTSTR lpResourceName)
{
  HRSRC hSrc = FindResource(hInst, lpResourceName, RT_BITMAP);
  BOOL bSuccess = FALSE;
  if (hSrc)
  {
    HGLOBAL hResData = LoadResource(hInst, hSrc);
    if (hResData)
    {
      LPVOID lpResData = LockResource(hResData);
      if (lpResData)
      {
        DWORD dwSize = SizeofResource(hInst, hSrc);
        if (dwSize)
        {
          HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwSize);
          if (hGlobal)
          {
            LPVOID lpBitmapData = GlobalLock(hGlobal);
            if (lpBitmapData)
            {
               CopyMemory(lpBitmapData, lpResData, dwSize);
               GlobalUnlock(hGlobal);
               bSuccess = Attach((HDIB) hGlobal);
            }
          }
        }
      }
    }
  }
  return bSuccess;
}


int CDibImage::GetBitsPerPixel() const
{
  int nBitCount;
  if (m_hDib == NULL)
    nBitCount = 0;
  else
  {
    LPSTR lpDib = (LPSTR) ::GlobalLock(m_hDib);
  
    //  Calculate the number of bits per pixel for the DIB.
    if (IS_WIN30_DIB(lpDib))
    {
      LPBITMAPINFOHEADER pBih = (LPBITMAPINFOHEADER)lpDib;
      nBitCount = pBih->biBitCount;
    }
    else
    {
      LPBITMAPCOREHEADER pbic = (LPBITMAPCOREHEADER)lpDib;
      nBitCount = pbic->bcBitCount;
    }
  
    ::GlobalUnlock((HGLOBAL) m_hDib);
  }
  return nBitCount;
}


BOOL CDibImage::IsRunLengthEncoded() const
{
  BOOL bEncoded = FALSE;
  if (m_hDib)
  {
    LPSTR lpDib = (LPSTR) ::GlobalLock(m_hDib);
  
    //  Calculate the number of bits per pixel for the DIB.
    if (IS_WIN30_DIB(lpDib))
    {
      LPBITMAPINFOHEADER lpbih = (LPBITMAPINFOHEADER) lpDib;
      bEncoded = (lpbih->biCompression == BI_RLE8) || (lpbih->biCompression == BI_RLE4);
    }
    ::GlobalUnlock((HGLOBAL) m_hDib);
  }

  return bEncoded;
}


BOOL CDibImage::Draw(CDC* dc, const CRect* rcDst, const CRect* rcSrc, CPalette* pPal)
{
  CRect DCRect(Rect());
  if (rcDst)
    DCRect = *rcDst;

  CRect DibRect(Rect());
  if (rcSrc)
    DibRect = *rcSrc;

  CPalette pal;
  pal.Attach(m_Pal);
  CPalette* pPalette = NULL;
  if (pPal)
    pPalette = pPal;
  else
    pPalette = &pal;

  BOOL bSuccess = ::DrawDIB(dc->m_hDC, &DCRect, m_hDib,	&DibRect,	pPalette);
  pal.Detach();
  return bSuccess;
}


// Currently only support pixel access for 1, 4, 8 & 24 images.
// Also note that access to RLE images is not supported
BOOL CDibImage::DataFormatSupported(void) const
{
  if (!IsRunLengthEncoded() &&
       ((m_nBitsPerPixel == 1)  || 
        (m_nBitsPerPixel == 4)  || 
        (m_nBitsPerPixel == 8)  || 
        (m_nBitsPerPixel == 24)) )
    return TRUE;
  else
    return FALSE;
}


BOOL CDibImage::FilteringFormatSupported(void) const
{
  if (m_nBitsPerPixel == 24)
    return TRUE;
  else
    return FALSE;
}


LPBYTE CDibImage::GetPixelAddress(int x, int y, LPBYTE lpDibBits) const
{
  BYTE* pData = NULL;

  if (!IsRunLengthEncoded())
  {
    switch (m_nBitsPerPixel)
    {
      case 1:
      {
        pData = lpDibBits + ((m_nHeight - 1) - y) * m_nScanWidth + (x/8);
        break;
      }
      case 4:
      {
        pData = lpDibBits + ((m_nHeight - 1) - y) * m_nScanWidth + (x/2);
        break;
      }
      case 8:
      {
        pData = lpDibBits + ((m_nHeight - 1) - y) * m_nScanWidth + (x);
        break;
      }
      case 24:
      {
        pData = lpDibBits + ((m_nHeight - 1) - y) * m_nScanWidth + (x*3);
        break;
      }
      default:
      {
        pData = NULL;
        break;
      }
    }
  }
  return pData;
}


inline BYTE GetLoNibble(BYTE Data)
{
  return (BYTE) (Data & 0x0F);
}


inline BYTE GetHiNibble(BYTE Data)
{
  return (BYTE) ((Data >> 4) & 0x0F);
}


inline void SetLoNibble( BYTE& DstData ,BYTE SrcData )
{
  DstData = (BYTE) ((DstData & 0xF0) | (SrcData & 0x0F));
}


inline void SetHiNibble( BYTE& DstData ,BYTE SrcData )
{
  DstData = (BYTE) ((DstData & 0x0F) | ((SrcData << 4) & 0xF0));
}


BOOL CDibImage::GetPixelData(int x, int y, ULONG& value, LPSTR lpDibBits) const
{
  if (m_hDib == NULL)
    return FALSE;

  if (DataFormatSupported() == FALSE)
    return FALSE;

  //position is out of range
  if (x >= m_nWidth || x < 0 || y >= m_nHeight || y < 0)
    return FALSE;

  BOOL bLocked = FALSE;
  if (lpDibBits == NULL)
  {
    LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
    lpDibBits = ::FindDIBBits(lpDibHdr);
    bLocked = TRUE;
  }

  BYTE* pData = GetPixelAddress(x,y,(BYTE*) lpDibBits);

  switch (m_nBitsPerPixel)
  {
    case 1:
    {
      x = x % 8;
      value = ( pData[0] << x ) & 0x80 ? 1 : 0;
      break;
    }
    case 4:
    {
      if (x % 2) 
         value = GetLoNibble(pData[0]);
      else
         value = GetHiNibble(pData[0]);
      break;
    }
    case 8:
    {
      value = pData[0];
      break;
    }
    case 24:
    {
      value = RGB(pData[2], pData[1], pData[0]);
      break;
    }
    default:
    {
      return FALSE;
      break;
    }
  }

  if (bLocked)
    ::GlobalUnlock((HGLOBAL) m_hDib);

  return TRUE;
}


BOOL CDibImage::SetPixelData(int x, int y, const ULONG& value, LPSTR lpDibBits)
{
  if (m_hDib == NULL)
    return FALSE;

  if (DataFormatSupported() == FALSE)
    return FALSE;

  //check the position
  ASSERT(x < m_nWidth && x >= 0 && y < m_nHeight && y >= 0);

  BOOL bLocked = FALSE;
  if (lpDibBits == NULL)
  { 
    LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
    lpDibBits = ::FindDIBBits(lpDibHdr);
    bLocked = TRUE;
  }

  BYTE* pData = GetPixelAddress(x, y, (BYTE*)lpDibBits);

  switch (m_nBitsPerPixel)
  {
    case 1:
    {
      x = x % 8;
      if ( value )
         pData[0] = (BYTE) (pData[0] | (0x80 >> x)); 
      else
         pData[0] = (BYTE) (pData[0] & ~(0x80 >> x)); 
      break;
    }
    case 4:
    {
      if (x % 2) 
         SetLoNibble(pData[0], (BYTE)value);
      else
         SetHiNibble(pData[0], (BYTE)value);      
      break;
    }
    case 8:
    {
      pData[0] = (BYTE) value;
      break;
    }
    case 24:
    {
      pData[0] = GetBValue(value);
      pData[1] = GetGValue(value);
      pData[2] = GetRValue(value);
      break;
    }
    default:
    {
      return FALSE;
    }
  }

  if (bLocked)
    ::GlobalUnlock((HGLOBAL) m_hDib);

  return TRUE;
}


BOOL CDibImage::GetPixel(int x, int y, COLORREF& value, LPSTR lpDibBits) const
{
  if (m_hDib == NULL)
    return FALSE;

  if (m_nBitsPerPixel != 24)
    return FALSE;

  //position is out of range
  if (x >= m_nWidth || x < 0 || y >= m_nHeight || y < 0)
    return FALSE;

  BOOL bLocked = FALSE;
  if (lpDibBits == NULL)
  {
    LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
    lpDibBits = ::FindDIBBits(lpDibHdr);
    bLocked = TRUE;
  }

  BYTE* pData = (LPBYTE) lpDibBits + (m_nHeight - 1 - y ) * m_nScanWidth + (x * 3);

  value = RGB(pData[2], pData[1], pData[0]);

  if (bLocked)
    ::GlobalUnlock((HGLOBAL) m_hDib);

  return TRUE;
}


BOOL CDibImage::SetPixel(int x, int y, const COLORREF& value, LPSTR lpDibBits)
{
  if (m_hDib == NULL)
    return FALSE;

  if ((m_nBitsPerPixel != 24))
    return FALSE;

  //check the position
  ASSERT(x < m_nWidth && x >= 0 && y < m_nHeight && y >= 0);

  BOOL bLocked = FALSE;
  if (lpDibBits == NULL)
  {
    LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
    lpDibBits = ::FindDIBBits(lpDibHdr);
    bLocked = TRUE;
  }

  BYTE* pData = GetPixelAddress(x, y, (BYTE*)lpDibBits);
  BOOL bSuccess = SetPixelData(x, y, value, lpDibBits);

  if (bLocked)
    ::GlobalUnlock((HGLOBAL) m_hDib);

  return TRUE;
}


BOOL CDibImage::Save(LPCTSTR lpszPathName)
{
  if (m_hDib == NULL)
    return FALSE;

  //Determine the file format to use based on the filename extension
  TCHAR pszExt[_MAX_EXT];
  _tsplitpath(lpszPathName, NULL, NULL, NULL, pszExt);
  CString sExt(pszExt);   
	if ((sExt.CompareNoCase(_T(".bmp")) == 0) || (sExt.CompareNoCase(_T(".dib")) == 0))
	{
    CFile f;
		if (!f.Open(lpszPathName, CFile::shareDenyWrite | CFile::modeCreate | CFile::modeWrite))
			return FALSE;
		else
			return SaveDIBFile(m_hDib, f);	
	}
//#ifndef DIBIMAGE_NO_JPEG
//	else if ((sExt.CompareNoCase(_T(".jpg")) == 0) || (sExt.CompareNoCase(_T(".jpeg")) == 0))
//	{
//    CFile f;
//		if (!f.Open(lpszPathName, CFile::shareDenyWrite | CFile::modeCreate | CFile::modeWrite))
//			return FALSE;
//		else
//			return SaveJPEGFile(m_hDib, f);
//	}
//#endif
  else
  {
    TRACE(_T("Could not determine what file format to save as\n"));
    return FALSE; //unknown file format
  }
}


BOOL CDibImage::CopySelection(CDibImage& dib)
{
  if (m_hDib == NULL)
    return FALSE;

  BOOL bSuccess = FALSE;
  ASSERT(m_pWorkingArea);
  CRect r = m_pWorkingArea->BoundingRectangle();
  bSuccess = dib.Create(CSize(r.Width(), r.Height()), 24);

  if (bSuccess)
  {
    LPSTR lpDibHdrSrc  = (LPSTR) ::GlobalLock(m_hDib);
    LPSTR lpDibBitsSrc = ::FindDIBBits(lpDibHdrSrc);
    LPSTR lpDibHdrDest  = (LPSTR) ::GlobalLock(dib.m_hDib);
    LPSTR lpDibBitsDest = ::FindDIBBits(lpDibHdrDest);
    for (int y=r.top; bSuccess && y<r.bottom; y++)
    {
      for (int x=r.left; bSuccess && x<r.right; x++)
      {
        if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
        {
          COLORREF c;
          bSuccess = GetPixel(x, y, c, lpDibBitsSrc);
          bSuccess = bSuccess && dib.SetPixel(x-r.left, y-r.top, c, lpDibBitsDest);
        }
      }
    }
    GlobalUnlock((HGLOBAL) dib.m_hDib);
    GlobalUnlock((HGLOBAL) m_hDib);
  }

  return bSuccess;
}


LPSTR CDibImage::GetDIBBits()
{
  LPSTR lpDibHdrSrc  = (LPSTR) ::GlobalLock(m_hDib);
  return ::FindDIBBits(lpDibHdrSrc);
}


BOOL CDibImage::CopyToClipboard()
{
  if (m_hDib == NULL)
    return FALSE;

  BOOL bSuccess = FALSE;
  if (OpenClipboard(NULL))
  {
    if (EmptyClipboard())
    {
      CDibImage DibCopy;
      bSuccess = CopySelection(DibCopy);
      if (bSuccess)
      {
        HDIB hNewDib = (HDIB) CopyHandle(DibCopy.m_hDib);
        bSuccess = (::SetClipboardData(CF_DIB, hNewDib) != NULL);
      }
    }
    CloseClipboard();
  }
  return bSuccess;
}


BOOL CDibImage::PasteFromClipboard()
{
  BOOL bSuccess = FALSE;
  if (OpenClipboard(NULL))
  {
    HDIB hNewDib = (HDIB) CopyHandle(::GetClipboardData(CF_DIB));
    CloseClipboard();
    if (hNewDib != NULL)
       bSuccess = Attach(hNewDib);
  }

  return bSuccess;
}


BOOL CDibImage::PasteAvailable()
{
  return IsClipboardFormatAvailable(CF_DIB);
}


void CDibImage::SetUndoSize(int nUndoSize)
{
  m_nUndoSize = nUndoSize;

  if (m_nUndoSize < m_UndoStack.GetSize())
    TRACE(_T("Losing some of the undo stack in CDibImage::SetUndoSize\n"));

  int i=m_nUndoSize;
  for (; i<m_UndoStack.GetSize(); i++)
  {
    CUndoNode* pNode = m_UndoStack.GetAt(i);
    delete pNode;
  }

  for (i=m_nUndoSize; i<m_RedoStack.GetSize(); i++)
  {
    CUndoNode* pNode = m_RedoStack.GetAt(i);
    delete pNode;
  }

  //Trim back the size of the arrays if necessary
  if (m_UndoStack.GetSize() > nUndoSize)
    m_UndoStack.SetSize(m_nUndoSize);

  if (m_RedoStack.GetSize() > nUndoSize)
    m_RedoStack.SetSize(m_nUndoSize);
}


BOOL CDibImage::SaveState(UINT nID)
{
  CString sDescription;
  sDescription.LoadString(nID);
  return SaveState(sDescription);
}


BOOL CDibImage::SaveState(const CString& sDescription)
{
  //if there is no undo stack then return immediately
  if (m_nUndoSize == 0)
	return FALSE;

  //If we have reached the limit of the undo stack
  //then drop the oldest undo by moving all the items
  //down in the stack
  if (m_UndoStack.GetSize() == m_nUndoSize)
  {
    TRACE(_T("Undo stack size exceeded, discarding oldest\n"));
    CUndoNode* pNode = m_UndoStack.GetAt(0);
    delete pNode;
  
    for (int i=1; i<m_UndoStack.GetSize(); i++)
    {
      CUndoNode* pNode = m_UndoStack.GetAt(i);
      m_UndoStack.SetAt(i-1, pNode);
    }  
    m_UndoStack.SetSize(m_nUndoSize-1);
  }

  CDibImage* pCopy = new CDibImage(*this);
  CUndoNode* pNode = new CUndoNode(pCopy, sDescription);
  m_UndoStack.Add(pNode);
  return TRUE;
}


BOOL CDibImage::UndoAvailable()
{
  return (m_UndoStack.GetSize() > 0);
}


BOOL CDibImage::Undo()
{
  BOOL bSuccess = FALSE;
  int nIndex = m_UndoStack.GetUpperBound();
  if (nIndex >= 0)
  {
    //Add the current image to the redo stack
    CDibImage* pCopy = new CDibImage(*this);
    CUndoNode* pNode1 = new CUndoNode(pCopy, m_sCurrentDescription);
    m_RedoStack.Add(pNode1);
  
    //Remove from the undo stack and 
    //reinstate the current value
    CUndoNode* pNode2 = m_UndoStack.GetAt(nIndex);
    operator=(*pNode2->GetImage());
    pNode1->SetDescription(pNode2->GetDescription());
    delete pNode2;
    m_UndoStack.RemoveAt(nIndex);
  
    bSuccess = TRUE;
  }
  return bSuccess;
}


BOOL CDibImage::Redo()
{
  BOOL bSuccess = FALSE;
  int nIndex = m_RedoStack.GetUpperBound();
  if (nIndex >= 0)
  {
    //Add the current image to the undo stack
    CDibImage* pCopy = new CDibImage(*this);
    CUndoNode* pNode1 = new CUndoNode(pCopy, m_sCurrentDescription);
    m_UndoStack.Add(pNode1);
  
    //Remove from the redo stack and 
    //reinstate the current value
    CUndoNode* pNode2 = m_RedoStack.GetAt(nIndex);
    operator=(*pNode2->GetImage());
    pNode1->SetDescription(pNode2->GetDescription());
    delete pNode2;
    m_RedoStack.RemoveAt(nIndex);
  
    bSuccess = TRUE;
  }
  return bSuccess;
}


BOOL CDibImage::RedoAvailable()
{
  return (m_RedoStack.GetSize() > 0);
}


CString CDibImage::UndoDescription() const
{
  CString rVal;
  int nIndex = m_UndoStack.GetUpperBound();
  if (nIndex >= 0)
  {
    CUndoNode* pNode = m_UndoStack.GetAt(nIndex);
    rVal = pNode->GetDescription();
  }
  return rVal;
}


CString CDibImage::RedoDescription() const
{
  CString rVal;
  int nIndex = m_RedoStack.GetUpperBound();
  if (nIndex >= 0)
  {
    CUndoNode* pNode = m_RedoStack.GetAt(nIndex);
    rVal = pNode->GetDescription();
  }
  return rVal;
}


CWorkingArea* CDibImage::GetWorkingArea()
{
  return m_pWorkingArea;
}


void CDibImage::SetWorkingArea(CWorkingArea* pWorkingArea)
{
  if (m_pWorkingArea)
  {
    delete m_pWorkingArea;
    m_pWorkingArea = NULL;
  }
  m_pWorkingArea = pWorkingArea;
}


BOOL CDibImage::SetColor(COLORREF color)
{
  if (m_hDib == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_SETCOLOR);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;

  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
        bSuccess = SetPixel(x, y, color, lpDibBits);
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::Flip()
{
  if (m_hDib == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_FLIP);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);

  int nMiddle = r.Height()/2 + r.top;
  for (int y=nMiddle; bSuccess && y>r.top; y--)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c1;
        int y1 = nMiddle + (nMiddle - y);
        bSuccess = GetPixelData(x, y1, c1, lpDibBits);

        COLORREF c2;
        bSuccess = bSuccess && GetPixelData(x, y, c2, lpDibBits);

        bSuccess = bSuccess && SetPixelData(x, y, c1, lpDibBits);
        bSuccess = bSuccess && SetPixelData(x, y1, c2, lpDibBits);
      }
    }
  }

  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::Mirror()
{
  if (m_hDib == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_MIRROR);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);

  int nMiddle = r.Width()/2 + r.left;
  for (int x=nMiddle; bSuccess && x>r.left; x--)
  {
    for (int y=r.top; bSuccess && y<r.bottom; y++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c1;
        int x1 = nMiddle + (nMiddle - x);
        bSuccess = GetPixelData(x1, y, c1, lpDibBits);

        COLORREF c2;
        bSuccess = bSuccess && GetPixelData(x, y, c2, lpDibBits);

        bSuccess = bSuccess && SetPixelData(x, y, c1, lpDibBits);
        bSuccess = bSuccess && SetPixelData(x1, y, c2, lpDibBits);
      }
    }
  }

  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::AdjustBrightness(int Percentage)
{
  if (m_hDib == NULL)
    return FALSE;

  if (Percentage < 0)
    return FALSE;

//  SaveState(IDS_DI_UNDO_ADJUST_BRIGHTNESS);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          int r = min(GetRValue(c)*Percentage/100, 255);
          int g = min(GetGValue(c)*Percentage/100, 255);
          int b = min(GetBValue(c)*Percentage/100, 255);
          r = max(r, 0);
          g = max(g, 0);
          b = max(b, 0);
          c = RGB(r, g, b);
          bSuccess = SetPixel(x, y, c, lpDibBits);
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::AdjustContrast(int Percentage)
{
  if (m_hDib == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_CONTRAST);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;

  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        bSuccess = GetPixel(x, y, c, lpDibBits);
        int r = min(128 + ((GetRValue(c) - 128)*Percentage/100), 255);
        int g = min(128 + ((GetGValue(c) - 128)*Percentage/100), 255);
        int b = min(128 + ((GetBValue(c) - 128)*Percentage/100), 255);
        r = max(r, 0);
        g = max(g, 0);
        b = max(b, 0);
        c = RGB(r, g, b);
        bSuccess = SetPixel(x, y, c, lpDibBits);
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::AdjustGammaCorrection(float Value)
{
  if (m_hDib == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_GAMMA);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  double MaxRange = pow((double) 255, (double) Value) / 255;


  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        bSuccess = GetPixel(x, y, c, lpDibBits);

        double dblR = pow((double) GetRValue(c), (double) Value) / MaxRange;
        double dblG = pow((double) GetGValue(c), (double) Value) / MaxRange;
        double dblB = pow((double) GetBValue(c), (double) Value) / MaxRange;

        int r = min((int) (dblR+0.5), 255);
        int g = min((int) (dblG+0.5), 255);
        int b = min((int) (dblB+0.5), 255);
        r = max(r, 0);
        g = max(g, 0);
        b = max(b, 0);
        c = RGB(r, g, b);
        bSuccess = SetPixel(x, y, c, lpDibBits);
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::AdjustHighLight(int Percentage)
{
  if (m_hDib == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_HIGHLIGHT);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;

  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        bSuccess = GetPixel(x, y, c, lpDibBits);
        int r = GetRValue(c);
        int g = GetGValue(c);
        int b = GetBValue(c);
        int l = r+g+b;
        if ((l > (170*3)))
        {
          r = min(r*Percentage/100, 255);
          g = min(g*Percentage/100, 255);
          b = min(b*Percentage/100, 255);
          r = max(r, 0);
          g = max(g, 0);
          b = max(b, 0);
          c = RGB(r, g, b);
          bSuccess = SetPixel(x, y, c, lpDibBits);
        }
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::AdjustMidtone(int Percentage)
{
  if (m_hDib == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_MIDTONE);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;

  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        bSuccess = GetPixel(x, y, c, lpDibBits);
        int r = GetRValue(c);
        int g = GetGValue(c);
        int b = GetBValue(c);
        int l = r+g+b;
        if ((l >= (85*3)) && (l <= (170*3)))
        {
          r = min(r*Percentage/100, 255);
          g = min(g*Percentage/100, 255);
          b = min(b*Percentage/100, 255);
          r = max(r, 0);
          g = max(g, 0);
          b = max(b, 0);
          c = RGB(r, g, b);
          bSuccess = SetPixel(x, y, c, lpDibBits);
        }
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::AdjustShadow(int Percentage)
{
  if (m_hDib == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_SHADOW);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;

  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        bSuccess = GetPixel(x, y, c, lpDibBits);
        int r = GetRValue(c);
        int g = GetGValue(c);
        int b = GetBValue(c);
        int l = r+g+b;
        if (l < (85*3))
        {
          r = min(r*Percentage/100, 255);
          g = min(g*Percentage/100, 255);
          b = min(b*Percentage/100, 255);
          r = max(r, 0);
          g = max(g, 0);
          b = max(b, 0);
          c = RGB(r, g, b);
          bSuccess = SetPixel(x, y, c, lpDibBits);
        }
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::AdjustHue(int Percentage)
{
  if (m_hDib == NULL)
    return FALSE;

  if (Percentage < 0)
    return FALSE;

//  SaveState(IDS_DI_UNDO_ADJUST_HUE);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          double H;
          double S;
          double L;
          RGBtoHSL(c, &H, &S, &L);
          H = (H*Percentage/100);
          bSuccess = SetPixel(x, y, HLStoRGB(H, L, S), lpDibBits);
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::AdjustSaturation(int Percentage)
{
  if (m_hDib == NULL)
    return FALSE;

  if (Percentage < 0)
    return FALSE;

//  SaveState(IDS_DI_UNDO_ADJUST_SATURATION);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          double H;
          double S;
          double L;
          RGBtoHSL(c, &H, &S, &L);
          S = (S*Percentage/100);
          bSuccess = SetPixel(x, y, HLStoRGB(H, L, S), lpDibBits);
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::AdjustHSL(int PercentHue, int PercentSaturation, int PercentLuminosity)
{
  if (m_hDib == NULL)
    return FALSE;

  if (PercentHue < 0)
    return FALSE;

  if (PercentSaturation < 0)
    return FALSE;

  if (PercentLuminosity < 0)
    return FALSE;


//  SaveState(IDS_DI_UNDO_ADJUST_HSL);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          double H;
          double S;
          double L;
          RGBtoHSL(c, &H, &S, &L);
          S = (S*PercentSaturation/100);
          H = (H*PercentHue/100);
          L = (L*PercentLuminosity/100);
          bSuccess = SetPixel(x, y, HLStoRGB(H, L, S), lpDibBits);
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


WORD CDibImage::GetVersion()
{
  return 0x0110;  //ie v1.10     
   
  /* Revision History
  1.0  18/11/1997:  Initial Public Release
  1.01 10/11/1998:  See help file for changes
  1.1  24/07/1999:  See help file for changes
  */
}


void CDibImage::RGBtoHSL(COLORREF rgb, double* H, double* S, double* L)
{
  double delta;
  double r = (double)GetRValue(rgb)/255;
  double g = (double)GetGValue(rgb)/255;
  double b = (double)GetBValue(rgb)/255;
  double cmax = max(r,max(g,b));
  double cmin = min(r,min(g,b));
  *L = (cmax+cmin)/2.0;
  if (cmax == cmin) 
  {
    *S = 0;
    *H = 0; // it's really undefined
  } 
  else 
  {
    if (*L < 0.5) 
       *S = (cmax-cmin)/(cmax+cmin);
    else
       *S = (cmax-cmin)/(2.0-cmax-cmin);
    delta = cmax - cmin;
    if (r==cmax)
       *H = (g-b)/delta;
    else if (g==cmax)
       *H = 2.0 +(b-r)/delta;
    else
       *H = 4.0+(r-g)/delta;
    *H /= 6.0;
    if (*H < 0.0)
       *H += 1;
  }
}


double CDibImage::HuetoRGB(double m1, double m2, double h)
{
  if (h < 0) 
    h += 1.0;
  if (h > 1) 
    h -= 1.0;
  if (6.0*h < 1)
    return (m1+(m2-m1)*h*6.0);
  if (2.0*h < 1)
    return m2;
  if (3.0*h < 2.0)
    return (m1+(m2-m1)*((2.0/3.0)-h)*6.0);
  return m1;
}


COLORREF CDibImage::HLStoRGB(const double& H, const double& L, const double& S)
{
  double r,g,b;
  double m1, m2;

  if (S==0) 
  {
    r=g=b=L;
  } 
  else 
  {
    if (L <= 0.5)
       m2 = L*(1.0+S);
    else
       m2 = L+S-L*S;
    m1 = 2.0*L-m2;
    r = HuetoRGB(m1, m2, H+1.0/3.0);
    g = HuetoRGB(m1, m2, H);
    b = HuetoRGB(m1, m2, H-1.0/3.0);
  }
  return RGB((BYTE)(r*255),(BYTE)(g*255),(BYTE)(b*255));
}


BOOL CDibImage::AdjustRed(int Percentage)
{
  if (m_hDib == NULL)
    return FALSE;

  if (Percentage < 0)
    return FALSE;

//  SaveState(IDS_DI_UNDO_ADJUST_RED);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          int r = min(GetRValue(c)*Percentage/100, 255);
          r = max(r, 0);
          c = RGB(r, GetGValue(c), GetBValue(c));
          bSuccess = SetPixel(x, y, c, lpDibBits);
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::AdjustGreen(int Percentage)
{
  if (m_hDib == NULL)
    return FALSE;

  if (Percentage < 0)
    return FALSE;

//  SaveState(IDS_DI_UNDO_ADJUST_GREEN);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          int g = min(GetGValue(c)*Percentage/100, 255);
          g = max(g, 0);
          c = RGB(GetRValue(c), g, GetBValue(c));
          bSuccess = SetPixel(x, y, c, lpDibBits);
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::AdjustBlue(int Percentage)
{
  if (m_hDib == NULL)
    return FALSE;

  if (Percentage < 0)
    return FALSE;

//  SaveState(IDS_DI_UNDO_ADJUST_BLUE);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          int b = min(GetBValue(c)*Percentage/100, 255);
          b = max(b, 0);
          c = RGB(GetRValue(c), GetGValue(c), b);
          bSuccess = SetPixel(x, y, c, lpDibBits);
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::Grayscale()
{
  if (m_hDib == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_ADJUST_GREYSCALE);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          int val = ((GetRValue(c) + GetGValue(c) + GetBValue(c)) / 3);
          bSuccess = SetPixel(x, y, RGB(val, val, val), lpDibBits);
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::Negate()
{
  if (m_hDib == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_ADJUST_NEGATE);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
          bSuccess = SetPixel(x, y, RGB(255 - GetRValue(c), 255 - GetGValue(c), 255 - GetBValue(c)), lpDibBits);
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::FindEdgesFilter()
{
  if (m_hDib == NULL)
    return FALSE;

  C3By3Filter Filter1;
  Filter1.m_nValues[0][0] = -1;
  Filter1.m_nValues[0][1] = 0;
  Filter1.m_nValues[0][2] = 1;
  Filter1.m_nValues[1][0] = -2;
  Filter1.m_nValues[1][1] = 0;
  Filter1.m_nValues[1][2] = 2;
  Filter1.m_nValues[2][0] = -1;
  Filter1.m_nValues[2][1] = 0;
  Filter1.m_nValues[2][2] = 1;

  C3By3Filter Filter2;
  Filter2.m_nValues[0][0] = -1;
  Filter2.m_nValues[0][1] = -2;
  Filter2.m_nValues[0][2] = -1;
  Filter2.m_nValues[1][0] = 0;
  Filter2.m_nValues[1][1] = 0;
  Filter2.m_nValues[1][2] = 0;
  Filter2.m_nValues[2][0] = 1;
  Filter2.m_nValues[2][1] = 2;
  Filter2.m_nValues[2][2] = 1;

  CDibImage dibCopy;
  CopySelection(dibCopy);

//  SaveState(IDS_DI_UNDO_FILTER);

  ASSERT(dibCopy.m_pWorkingArea);
  CRect rectSrc(dibCopy.m_pWorkingArea->BoundingRectangle());
  CRect rectDest(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdrSrc   = (LPSTR) ::GlobalLock(dibCopy.m_hDib);
  LPSTR lpDibBitsSrc  = ::FindDIBBits(lpDibHdrSrc);
  LPSTR lpDibHdrDest  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBitsDest = ::FindDIBBits(lpDibHdrDest);
  for (int y=0; bSuccess && y<rectSrc.Height(); y++)
  {
    for (int x=0; bSuccess && x<rectSrc.Width(); x++)
    {
      if (dibCopy.m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c1 = Filter1.Filter(dibCopy, lpDibBitsSrc, x, y);
        COLORREF c2 = Filter2.Filter(dibCopy, lpDibBitsSrc, x, y);
        int r = max(GetRValue(c1), GetRValue(c2));
        int g = max(GetGValue(c1), GetGValue(c2));
        int b = max(GetBValue(c1), GetBValue(c2));
        bSuccess = SetPixel(x+rectDest.left, y+rectDest.top, RGB(r, g, b), lpDibBitsDest);
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::FindVerticalEdgesFilter()
{
  C3By3Filter Filter;
  Filter.m_nValues[0][0] = -1;
  Filter.m_nValues[0][1] = -2;
  Filter.m_nValues[0][2] = -1;
  Filter.m_nValues[1][0] = 0;
  Filter.m_nValues[1][1] = 0;
  Filter.m_nValues[1][2] = 0;
  Filter.m_nValues[2][0] = 1;
  Filter.m_nValues[2][1] = 2;
  Filter.m_nValues[2][2] = 1;

  return UserDefinedFilter(Filter);
}

BOOL CDibImage::FindHorizontalEdgesFilter()
{
  C3By3Filter Filter;
  Filter.m_nValues[0][0] = -1;
  Filter.m_nValues[0][1] = 0;
  Filter.m_nValues[0][2] = 1;
  Filter.m_nValues[1][0] = -2;
  Filter.m_nValues[1][1] = 0;
  Filter.m_nValues[1][2] = 2;
  Filter.m_nValues[2][0] = -1;
  Filter.m_nValues[2][1] = 0;
  Filter.m_nValues[2][2] = 1;

  return UserDefinedFilter(Filter);
}


BOOL CDibImage::BlurFilter()
{
  C7By7Filter Filter;

  for (int i=0; i<7; i++)
    for (int j=0; j<7; j++)
       Filter.m_nValues[i][j] = 0;
  Filter.m_nValues[3][3] = -4;
  Filter.m_nValues[2][2] = 1;
  Filter.m_nValues[2][3] = 4;
  Filter.m_nValues[2][4] = 1;
  Filter.m_nValues[3][2] = 4;
  Filter.m_nValues[3][4] = 4;
  Filter.m_nValues[4][2] = 1;
  Filter.m_nValues[4][3] = 4;
  Filter.m_nValues[4][4] = 1;
  Filter.m_nDivision = 16;

  return UserDefinedFilter(Filter);
}


BOOL CDibImage::AddNoiseFilter(int Percentage)
{
  if (Percentage <= 0 || Percentage > 100)
    return FALSE;

  if (m_hDib == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_ADD_NOISE);

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      int test = rand();  
      BOOL bPass = test < (RAND_MAX*Percentage/100);
      if (bPass && m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        int r = rand()*256/RAND_MAX - 128;
        int g = rand()*256/RAND_MAX - 128;
        int b = rand()*256/RAND_MAX - 128;

        if (GetPixel(x, y, c, lpDibBits))
          bSuccess = SetPixel(x, y, RGB(GetRValue(c) + r, GetGValue(c) + g, GetBValue(c) + b), lpDibBits);
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


BOOL CDibImage::MedianFilter()
{
  C3By3MedianFilter Filter;
  return UserDefinedFilter(Filter);
}


BOOL CDibImage::MeanFilter()
{
  C3By3MeanFilter Filter;
  return UserDefinedFilter(Filter);
}


BOOL CDibImage::UserDefinedFilter(CUserDefinedFilter& Filter)
{
  if (m_hDib == NULL)
    return FALSE;

  CDibImage dibCopy;
  CopySelection(dibCopy);

//  SaveState(IDS_DI_UNDO_FILTER);

  ASSERT(dibCopy.m_pWorkingArea);
  CRect rectSrc(dibCopy.m_pWorkingArea->BoundingRectangle());
  CRect rectDest(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdrSrc   = (LPSTR) ::GlobalLock(dibCopy.m_hDib);
  LPSTR lpDibBitsSrc  = ::FindDIBBits(lpDibHdrSrc);
  LPSTR lpDibHdrDest  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBitsDest = ::FindDIBBits(lpDibHdrDest);
  for (int y=0; bSuccess && y<rectSrc.Height(); y++)
  {
    for (int x=0; bSuccess && x<rectSrc.Width(); x++)
    {
      if (dibCopy.m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c = Filter.Filter(dibCopy, lpDibBitsSrc, x, y);
        bSuccess = SetPixel(x+rectDest.left, y+rectDest.top, c, lpDibBitsDest);
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  if (!bSuccess)
    Undo();

  return bSuccess;
}


int CDibImage::ColorsUsed() const
{
  if (m_hDib == NULL)
    return 0;

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  //Use a hash table to keep previously found colors, The
  //most ideal method would be to use a sorted array with 
  //a binary chop but MFC does not provide one and I don't
  //feel like coding up one just for the sake of this function <g>!.
  CMap<COLORREF, COLORREF, COLORREF, COLORREF> colorsTable;
  colorsTable.InitHashTable(r.Width() * r.Height());

  //Iterate through the working area to find all its colors
  BOOL bSuccess = TRUE;
  int nColors = 0;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixelData(x, y, c, lpDibBits))
        {
          //If the color is not already in the table,
          //then increment the number of colors and 
          //add it to the hash table
          COLORREF cFound;
          if (!colorsTable.Lookup(c, cFound))
          {
            ++nColors;
            colorsTable.SetAt(c, c);
          }
        }
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  return nColors;
}


BOOL CDibImage::SplitChannels(CDibImage& RedChannel, CDibImage& GreenChannel, CDibImage& BlueChannel)
{
  if (m_hDib == NULL)
    return FALSE;

  if (!RedChannel.Create(Size(), 24))
    return FALSE;

  if (!GreenChannel.Create(Size(), 24))
    return FALSE;

  if (!BlueChannel.Create(Size(), 24))
    return FALSE;

  BOOL bSuccess = TRUE;

  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  LPSTR lpDibHdrRed  = (LPSTR) ::GlobalLock(RedChannel.m_hDib);
  LPSTR lpDibBitsRed = ::FindDIBBits(lpDibHdrRed);
  LPSTR lpDibHdrGreen  = (LPSTR) ::GlobalLock(GreenChannel.m_hDib);
  LPSTR lpDibBitsGreen = ::FindDIBBits(lpDibHdrGreen);
  LPSTR lpDibHdrBlue  = (LPSTR) ::GlobalLock(BlueChannel.m_hDib);
  LPSTR lpDibBitsBlue = ::FindDIBBits(lpDibHdrBlue);

  CRect r(Rect());
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      COLORREF c;
      if (GetPixel(x, y, c, lpDibBits))
      {
        int r = GetRValue(c);
        int g = GetGValue(c);
        int b = GetBValue(c);
        bSuccess = bSuccess && RedChannel.SetPixel(x, y, RGB(r, r, r), lpDibBitsRed);
        bSuccess = bSuccess && GreenChannel.SetPixel(x, y, RGB(g, g, g), lpDibBitsGreen);
        bSuccess = bSuccess && BlueChannel.SetPixel(x, y, RGB(b, b, b), lpDibBitsBlue);
      }
      else
        bSuccess = FALSE;
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);
  GlobalUnlock((HGLOBAL) RedChannel.m_hDib);
  GlobalUnlock((HGLOBAL) GreenChannel.m_hDib);
  GlobalUnlock((HGLOBAL) BlueChannel.m_hDib);

  return bSuccess;
}


BOOL CDibImage::GetRedChannel(CDibImage& red)
{
  if (m_hDib == NULL)
    return FALSE;

  if (!red.Create(Size(), 24))
    return FALSE;

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  LPSTR lpDibHdrRed  = (LPSTR) ::GlobalLock(red.m_hDib);
  LPSTR lpDibBitsRed = ::FindDIBBits(lpDibHdrRed);

  CRect r(Rect());
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      COLORREF c;
      if (GetPixel(x, y, c, lpDibBits))
      {
        int r = GetRValue(c);
        bSuccess = red.SetPixel(x, y, RGB(r, r, r), lpDibBitsRed);
      }
      else
        bSuccess = FALSE;
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);
  GlobalUnlock((HGLOBAL) red.m_hDib);

  return bSuccess;
}


BOOL CDibImage::GetGreenChannel(CDibImage& green)
{
  if (m_hDib == NULL)
    return FALSE;

  if (!green.Create(Size(), 24))
    return FALSE;

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  LPSTR lpDibHdrGreen  = (LPSTR) ::GlobalLock(green.m_hDib);
  LPSTR lpDibBitsGreen = ::FindDIBBits(lpDibHdrGreen);

  CRect r(Rect());
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      COLORREF c;
      if (GetPixel(x, y, c, lpDibBits))
      {
        int g = GetGValue(c);
        bSuccess = green.SetPixel(x, y, RGB(g, g, g), lpDibBitsGreen);
      }
      else
        bSuccess = FALSE;
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);
  GlobalUnlock((HGLOBAL) green.m_hDib);

  return bSuccess;
}


BOOL CDibImage::GetBlueChannel(CDibImage& blue)
{
  if (m_hDib == NULL)
    return FALSE;

  if (!blue.Create(Size(), 24))
    return FALSE;

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  LPSTR lpDibHdrBlue  = (LPSTR) ::GlobalLock(blue.m_hDib);
  LPSTR lpDibBitsBlue = ::FindDIBBits(lpDibHdrBlue);

  CRect r(Rect());
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      COLORREF c;
      if (GetPixel(x, y, c, lpDibBits))
      {
        int b = GetBValue(c);
        bSuccess = blue.SetPixel(x, y, RGB(b, b, b), lpDibBitsBlue);
      }
      else
        bSuccess = FALSE;
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);
  GlobalUnlock((HGLOBAL) blue.m_hDib);

  return bSuccess;
}


BOOL CDibImage::CombineChannels(const CDibImage& red, const CDibImage& green, const CDibImage& blue)
{
  if (red.m_hDib == NULL || green.m_hDib == NULL || blue.m_hDib == NULL)
    return FALSE;

  //All the images should be the same size
  if (red.Rect() != green.Rect() || green.Rect() != blue.Rect() || red.Rect() != blue.Rect())
    return FALSE;

  if (!Create(red.Size(), 24))
    return FALSE;


  BOOL bSuccess = TRUE;

  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  LPSTR lpDibHdrRed  = (LPSTR) ::GlobalLock(red.m_hDib);
  LPSTR lpDibBitsRed = ::FindDIBBits(lpDibHdrRed);
  LPSTR lpDibHdrGreen  = (LPSTR) ::GlobalLock(green.m_hDib);
  LPSTR lpDibBitsGreen = ::FindDIBBits(lpDibHdrGreen);
  LPSTR lpDibHdrBlue  = (LPSTR) ::GlobalLock(blue.m_hDib);
  LPSTR lpDibBitsBlue = ::FindDIBBits(lpDibHdrBlue);

  CRect r(Rect());
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      COLORREF r;
      COLORREF g;
      COLORREF b;
      if (red.GetPixel(x, y, r, lpDibBitsRed) &&
        green.GetPixel(x, y, g, lpDibBitsGreen) &&
        blue.GetPixel(x, y, b, lpDibBitsBlue))
      {
        COLORREF c = RGB(GetRValue(r), GetGValue(g), GetBValue(b));
        bSuccess = bSuccess && SetPixel(x, y, c, lpDibBits);
      }
      else
        bSuccess = FALSE;
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);
  GlobalUnlock((HGLOBAL) red.m_hDib);
  GlobalUnlock((HGLOBAL) green.m_hDib);
  GlobalUnlock((HGLOBAL) blue.m_hDib);

  return bSuccess;
}


BOOL CDibImage::GetRedHistogram(int* RedChannel, int nSize)
{
  if (m_hDib == NULL)
    return FALSE;

  for (int i=0; i<nSize; i++)
    RedChannel[i] = 0;

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          int r = GetRValue(c);
          if (r < nSize)
            RedChannel[r]++;
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  return bSuccess;
}


BOOL CDibImage::GetGreenHistogram(int* GreenChannel, int nSize)
{
  if (m_hDib == NULL)
    return FALSE;

  for (int i=0; i<nSize; i++)
    GreenChannel[i] = 0;

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          int g = GetGValue(c);
          if (g < nSize)
            GreenChannel[g]++;
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  return bSuccess;
}


BOOL CDibImage::GetBlueHistogram(int* BlueChannel, int nSize)
{
  if (m_hDib == NULL)
    return FALSE;

  for (int i=0; i<nSize; i++)
    BlueChannel[i] = 0;

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          int b = GetBValue(c);
          if (b < nSize)
            BlueChannel[b]++;
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  return bSuccess;
}


BOOL CDibImage::GetHistogram(int* RedChannel, int nRedSize, int* GreenChannel, int nGreenSize, int* BlueChannel, int nBlueSize)
{
  if (m_hDib == NULL)
    return FALSE;

  int i=0;
  for (; i<nRedSize; i++)
    RedChannel[i] = 0;
  for (i=0; i<nGreenSize; i++)
    GreenChannel[i] = 0;
  for (i=0; i<nBlueSize; i++)
    BlueChannel[i] = 0;

  ASSERT(m_pWorkingArea);
  CRect r(m_pWorkingArea->BoundingRectangle());

  BOOL bSuccess = TRUE;
  LPSTR lpDibHdr  = (LPSTR) ::GlobalLock(m_hDib);
  LPSTR lpDibBits = ::FindDIBBits(lpDibHdr);
  for (int y=r.top; bSuccess && y<r.bottom; y++)
  {
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
      {
        COLORREF c;
        if (GetPixel(x, y, c, lpDibBits))
        {
          int r = GetRValue(c);
          int g = GetGValue(c);
          int b = GetBValue(c);
          if (r < nRedSize)
            RedChannel[r]++;
          if (g < nGreenSize)
            GreenChannel[g]++;
          if (b < nBlueSize)
            BlueChannel[b]++;
        }
        else
          bSuccess = FALSE;
      }
    }
  }
  GlobalUnlock((HGLOBAL) m_hDib);

  return bSuccess;
}


BOOL CDibImage::CopyFromBitmap(HBITMAP hBitmap, HPALETTE hPal)
{
  HDIB hDib = BitmapToDIB(hBitmap,hPal);
  if (hDib == NULL)
    return FALSE;
  return Attach(hDib);
}


HBITMAP CDibImage::CopyToBitmap(void)
{
  return DIBToBitmap(m_hDib, m_Pal);
}


BOOL CDibImage::CopyFromMemBmpFile(HMEMBMPFILE hMemBmpFile)
{
  HDIB hDib = MemBmpFileToDIB(hMemBmpFile);
  if (hDib == NULL)
    return FALSE;
  return Attach(hDib);
}


HMEMBMPFILE CDibImage::CopyToMemBmpFile(void)
{
  return DIBToMemBmpFile(m_hDib); 
}


BOOL CDibImage::CopyFromWindow(CWnd *pWnd, CRect* pScreenRect)
{
  HDIB hDib = WindowToDIB(pWnd, pScreenRect);
  if (hDib == NULL)
    return FALSE;
  return Attach(hDib);
}

BOOL CDibImage::CopyFromSrceen(CDC* dc, CRect* pScreenRect)
{
	HDIB hDib = SrceenDCToDIB(dc, pScreenRect);
	if (hDib == NULL)
		return FALSE;
	return Attach(hDib);
}




//HDIB WINAPI SrceenDCToDIB(CDC* dc, CRect  rect)
//{
//	CBitmap 	   bitmap;
//	CDC 		   memDC;
//
//	memDC.CreateCompatibleDC(dc); 
//
//	bitmap.CreateCompatibleBitmap(dc, rect.Width(),rect.Height() );
//
//	CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);
//	memDC.BitBlt(0, 0, rect.Width(),rect.Height(), dc, rect.left, rect.top, SRCCOPY); 
//
//	// Create logical palette if device support a palette
//	CPalette pal;
//	if ( dc->GetDeviceCaps(RASTERCAPS) & RC_PALETTE )
//	{
//		UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * MAXPALCOLORS);
//		LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize];
//		pLP->palVersion = PALVERSION;
//
//		pLP->palNumEntries = (USHORT) GetSystemPaletteEntries( dc->m_hDC, 0, MAXPALCOLORS-1, pLP->palPalEntry );
//
//		// Create the palette
//		pal.CreatePalette( pLP );
//
//		delete[] pLP;
//	}
//
//	memDC.SelectObject(pOldBitmap);
//
//	HDIB hDib = BitmapToDIB( bitmap, pal );
//
//	return hDib;
//}




BOOL CDibImage::ConvertTo24Bits(void)
{
  if (m_hDib == NULL)
    return FALSE;
  if (m_Pal == NULL)
    return FALSE;

  // check if it is just a 24 bits image
  if (m_nBitsPerPixel == 24)
    return TRUE;

  LOGPALETTE* pLogPalette = CreateLogPalette(m_Pal);

  if (pLogPalette == NULL)
    return FALSE;

//  SaveState(IDS_DI_UNDO_CONVERT24BITS);

  CDibImage dib;

  ASSERT(m_pWorkingArea);
  CRect r = m_pWorkingArea->BoundingRectangle();
  BOOL bSuccess = dib.Create(CSize(r.Width(), r.Height()), 24);

  if (bSuccess)
  {
    LPSTR lpDibHdrSrc  = (LPSTR) ::GlobalLock(m_hDib);
    LPSTR lpDibBitsSrc = ::FindDIBBits(lpDibHdrSrc);
    LPSTR lpDibHdrDest  = (LPSTR) ::GlobalLock(dib.m_hDib);
    LPSTR lpDibBitsDest = ::FindDIBBits(lpDibHdrDest);
    for (int x=r.left; bSuccess && x<r.right; x++)
    {
      for (int y=r.top; bSuccess && y<r.bottom; y++)
      {
        if (m_pWorkingArea->PointInSelection(CPoint(x, y)))
        {
          ULONG PixelData = 0;
          bSuccess = GetPixelData(x, y, PixelData, lpDibBitsSrc);
          PALETTEENTRY pe = pLogPalette->palPalEntry[PixelData];
          COLORREF ColorData = RGB( pe.peRed, pe.peGreen, pe.peBlue ); 
          bSuccess = bSuccess && dib.SetPixel(x-r.left, y-r.top, ColorData, lpDibBitsDest);
        }
      }
    }
    GlobalUnlock((HGLOBAL) dib.m_hDib);
    GlobalUnlock((HGLOBAL) m_hDib);

    if (Attach(dib.m_hDib))
      dib.m_hDib = NULL;
  }

  if (!bSuccess)
    Undo();

  DestroyLogPalette( pLogPalette );

  return bSuccess;
}
